import fs from 'node:fs';
import childProcess from 'node:child_process';
import _ from 'lodash';

export default class ExecuteHelper {
  static run = (
    command: string,
    options?: childProcess.ExecSyncOptions,
    config?: Record<string, string | number | boolean>,
  ) => {
    const execCommand = `${command} ${ExecuteHelper.buildConfigString(config).join(' ')}`;
    return childProcess.execSync(execCommand, {
      ...options,
      shell: ExecuteHelper.getEnabledShell(),
      stdio: [0, 1, 2],
      env: { ...process.env, ...options?.env },
    });
  };

  static asyncRun = (
    command: string,
    options?: childProcess.ExecSyncOptions,
    config?: Record<string, string | number | boolean>,
  ) => {
    const execCommand = `${command} ${ExecuteHelper.buildConfigString(config).join(' ')}`;
    const processInstance = childProcess.exec(execCommand, {
      ...options,
      shell: ExecuteHelper.getEnabledShell(),
      env: { ...process.env, ...options?.env },
    });
    const execPromise = new Promise((resolve, reject) => {
      processInstance.addListener('error', (error) => { reject(error); });
      processInstance.addListener('exit', (...args) => { resolve(args); });
    });
    return execPromise;
  };

  static runAndGetStdio = (
    command: string,
    options?: childProcess.ExecSyncOptions,
    config?: Record<string, string | number | boolean>,
  ) => {
    const execCommand = `${command} ${ExecuteHelper.buildConfigString(config).join(' ')}`;
    return childProcess.execSync(execCommand, {
      ...options,
      shell: ExecuteHelper.getEnabledShell(),
      env: { ...process.env, ...options?.env },
    }).toString().trim();
  };

  static getEnabledShell = () => {
    const shellCheckList = [
      '/bin/zsh',
      '/bin/ash',
      '/bin/bash',
      '/bin/sh',
    ];
    for (const shellPath of shellCheckList) {
      if (fs.existsSync(shellPath)) {
        return shellPath;
      }
    }
  };

  static buildConfigString = (config?: Record<string, string | number | boolean>) => {
    if (!config) {
      return [];
    }
    const configStrings = [];
    for (const key of Object.keys(config)) {
      const value = config[key];
      if (_.isBoolean(value)) {
        if (value === true) {
          configStrings.push(`--${key}`);
        } else {
          configStrings.push(`--${key}=false`);
        }
        continue;
      }
      configStrings.push(`--${key}=${value}`);
    }
    return configStrings;
  };
}
